from rv32_inst_parser import parse_rv32_inst
from rv32_inst_parser import csrs

versionInfo = {
    0:'0.11',
    1:'0.13'
}

dmistatInfo ={
    0: 'No Error',
    1: 'Reserved',
    2: 'An Operation Failed',
    3: 'Busy',
}

# ACK[1:0] in the DMI register (unlisted values are reserved)
ack_val = {
    0: 'Okay',
    1: 'Reserved',
    2: 'Failed',
    3: 'Busy'
}
# OP[1:0] in the DMI register
op_val = {
    0: 'Nop',
    1: 'Read',
    2: 'Write',
    3: 'Reserved'
}

# RISC-V DM registers
riscv_dm_regs = {
    0x04: ['data0',  'Astract Data 0'],
    0x05: ['data1',  'Astract Data 1'],
    0x06: ['data2',  'Astract Data 2'],
    0x07: ['data3',  'Astract Data 3'],
    0x08: ['data4',  'Astract Data 4'],
    0x09: ['data5',  'Astract Data 5'],
    0x0a: ['data6',  'Astract Data 6'],
    0x0b: ['data7',  'Astract Data 7'],
    0x0c: ['data8',  'Astract Data 8'],
    0x0d: ['data9',  'Astract Data 9'],
    0x0e: ['data10', 'Astract Data 10'],
    0x0f: ['data11', 'Astract Data 11'],
    0x10: ['dmcontrol', 'Debug Module Control'],
    0x11: ['dmstatus', 'Debug Module Status'],
    0x12: ['hartinfo', 'Hart Info'],
    0x13: ['haltsum1', 'Halt Summary 1'],
    0x14: ['hawindowsel', 'Hart Array Window Select'],
    0x15: ['hawindow', 'Hart Array Window'],
    0x16: ['abstractcs', 'Abstract Control and Status'],
    0x17: ['command', 'Absract Command'],
    0x18: ['abstractauto', 'Abstract Command Autoexec'],
    0x19: ['confstrptr0', 'Configuration String Pointer 0'],
    0x1a: ['confstrptr1', 'Configuration String Pointer 1'],
    0x1b: ['confstrptr2', 'Configuration String Pointer 2'],
    0x1c: ['confstrptr3', 'Configuration String Pointer 3'],
    0x1d: ['nextdm', 'Next Debug Module'],
    0x20: ['progbuf0', 'Program Buffer 0'],
    0x21: ['progbuf1', 'Program Buffer 1'],
    0x22: ['progbuf2', 'Program Buffer 2'],
    0x23: ['progbuf3', 'Program Buffer 3'],
    0x24: ['progbuf4', 'Program Buffer 4'],
    0x25: ['progbuf5', 'Program Buffer 5'],
    0x26: ['progbuf6', 'Program Buffer 6'],
    0x27: ['progbuf7', 'Program Buffer 7'],
    0x28: ['progbuf8', 'Program Buffer 8'],
    0x29: ['progbuf9', 'Program Buffer 9'],
    0x2a: ['progbuf10', 'Program Buffer 10'],
    0x2b: ['progbuf11', 'Program Buffer 11'],
    0x2c: ['progbuf12', 'Program Buffer 12'],
    0x2d: ['progbuf13', 'Program Buffer 13'],
    0x2e: ['progbuf14', 'Program Buffer 14'],
    0x2f: ['progbuf15', 'Program Buffer 15'],
    0x30: ['authdata', 'Authentication Data'],
    0x34: ['haltsum2', 'Halt Summary 2'],
    0x35: ['haltsum3', 'Halt Summary 3'],
    0x37: ['sbaddress3', 'System Bus Address 127:96'],
    0x38: ['sbcs', 'System Bus Access Control and Status'],
    0x39: ['sbaddress0', 'System Bus Address 31:0'],
    0x3a: ['sbaddress1', 'System Bus Address 63:32'],
    0x3b: ['sbaddress2', 'System Bus Address 95:64'],
    0x3c: ['sbdata0', 'System Bus Data 31:0'],
    0x3d: ['sbdata1', 'System Bus Data 63:32'],
    0x3e: ['sbdata2', 'System Bus Data 96:64'],
    0x3f: ['sbdata3', 'System Bus Data 127:96'],
    0x40: ['haltsum0', 'Halt Summary 0'],
}

# Regiser Numer sued in Access Register Command: [Register, ABI Name, Descrption, Saver]
rv32i_gprs = {
    0x1000: ['x0', 'zero', 'Zero Constant', '-'],
    0x1001: ['x1', 'ra', 'Return address', 'Caller'],
    0x1002: ['x2', 'sp', 'Stack pointer', '-'],
    0x1003: ['x3', 'gp', 'Global pointer', '-'],
    0x1004: ['x4', 'tp', 'Thread pointer', 'Callee'],
    0x1005: ['x5', 't0', 'Temporary 0', 'Caller'],
    0x1006: ['x6', 't1', 'Temporary 1', 'Caller'],
    0x1007: ['x7', 't2', 'Temporary 2', 'Caller'],
    0x1008: ['x8', 's0/fp', 'Saved register 0/frame pointer', 'Callee'],
    0x1009: ['x9', 's1', 'Saved register 1', 'Callee'],
    0x100a: ['x10', 'a0', 'Fn arg 0', 'Caller'],
    0x100b: ['x11', 'a1', 'Fn arg 1', 'Caller'],
    0x100c: ['x12', 'a2', 'Fn arg 2', 'Caller'],
    0x100d: ['x13', 'a3', 'Fn arg 3', 'Caller'],
    0x100e: ['x14', 'a4', 'Fn arg 4', 'Caller'],
    0x100f: ['x15', 'a5', 'Fn arg 5', 'Caller'],
    0x1010: ['x16', 'a6', 'Fn arg 6', 'Caller'],
    0x1011: ['x17', 'a7', 'Fn arg 7', 'Caller'],
    0x1012: ['x18', 's2', 'Saved register 2', 'Callee'],
    0x1013: ['x19', 's3', 'Saved register 3', 'Callee'],
    0x1014: ['x20', 's4', 'Saved register 4', 'Callee'],
    0x1015: ['x21', 's5', 'Saved register 5', 'Callee'],
    0x1016: ['x22', 's6', 'Saved register 6', 'Callee'],
    0x1017: ['x23', 's7', 'Saved register 7', 'Callee'],
    0x1018: ['x24', 's8', 'Saved register 8', 'Callee'],
    0x1019: ['x25', 's9', 'Saved register 9', 'Callee'],
    0x101a: ['x26', 's10', 'Saved register 10', 'Callee'],
    0x101b: ['x27', 's11', 'Saved register 11', 'Callee'],
    0x101c: ['x28', 't3', 'Temporary 3', 'Caller'],
    0x101d: ['x29', 't4', 'Temporary 4', 'Caller'],
    0x101e: ['x30', 't5', 'Temporary 5', 'Caller'],
    0x101f: ['x31', 't5', 'Temporary 5', 'Caller'],
}


cmd_types = {
    0: 'Access Register Command',
    1: 'Quick Access',
    2: 'Access Memory Command'
}

def parse_dtmcs(data, isWrite):
    version = data & 0xF
    abits = (data >> 4) & 0x3f
    dmistat = (data >> 10) & 0x3 
    idle = (data >> 12) & 0x03 
    dmireset = (data >> 16) & 0x1 
    dmihardreset = (data >> 17) & 1
    if (isWrite == False):
        retStr = 'Debug Version: ' + versionInfo.get(version, 'UNKNOwN') + '\n' + \
            '           Address Bits: ' + str(abits) + '\n' + \
            '           Required Run-test/Idle Cycles: ' + str(idle)  + '\n' + \
            '           dmireset = ' + str(dmireset) + '\n' +  \
            '           dmihardreset = ' + str(dmihardreset)
    else:
        retStr = 'dmireset = ' + str(dmireset) + \
            ' dmihardreset = ' + str(dmihardreset)
    return retStr

def parse_access_register_cmd(data):
    regno = data & 0xffff
    write = (data >> 16) & 1
    transfer = (data >> 17) & 1
    postexec = (data >> 18) & 1
    aarpostincrement = (data >> 19) & 1 
    aarsize = (data >> 20) & 7
    dataBytes = 4
    if (aarsize == 0):
        dataBytes = 1
    elif (aarsize == 1):
        dataBytes = 2
    if (aarsize == 2):
        dataBytes = 4
    elif (aarsize == 3):
        dataBytes = 8
    elif (aarsize == 4):
        dataBytes = 16
    if (regno >= 0x1000) and (regno <= 0x101f):
        regnoattr = rv32i_gprs.get(regno, ['unknon', 'unknown', 'unknown', '-'])
        regnoInfo = regnoattr[0] + '(' + regnoattr[1] + ')'
    else:
        regnoInfo = csrs.get(regno, [hex(regno), hex(regno)])[0]
    cmdStr = ''
    if (transfer > 0):
        cmdStr +='\n        ' + 'transfer: ' + hex(transfer)
    if (postexec == 1):
        cmdStr += '\n        ' + 'postexec: ' + hex(postexec)
    if (aarpostincrement == 1):
        cmdStr +=  '\n        ' + 'aarpostincrement: ' + hex(aarpostincrement)
    if (write == 0):
        cmdStr += ('\n        ' + 'copy ' + str(dataBytes) + ' bytes of data from ' + regnoInfo + ' to arg0(data0)')
    else:
        cmdStr += ('\n        ' + 'copy ' + str(dataBytes) + ' bytes of data from  arg0(data0) to ' + regnoInfo)
    return cmdStr

def parse_quick_access_cmd(data):
    return ''

def parse_accesss_memory_cmd(data):
    target_specific = (data >> 14) & 3 
    write = (data >> 15) & 1 
    aampostincrement = (data >> 19) & 1
    aamsize = (data >> 20) & 7 
    aamvirtual = (data >> 23) & 1
    cmdStr = '\n        ' + 'target_specific: ' + hex(target_specific) + \
        '\n        ' + 'write: ' + hex(write) + \
        '\n        ' + 'aampostincrement: ' + hex(aampostincrement) + \
        '\n        ' + 'aamsize: ' + hex(aamsize) + \
        '\n        ' + 'aamvirtual ' + hex(aamvirtual)
    return cmdStr
     


def parse_abstract_cmd(data, isWrite):
    cmdType = (data >> 24) & 0xff # data[31:24]
    control = data & 0xffffff # data[23:0]
    if (cmdType == 0):
        cmd_str = cmd_types.get(cmdType, 'Unknown') + ':' + parse_access_register_cmd(control)
    elif (cmdType == 1):
        cmd_str = cmd_types.get(cmdType, 'Unknown') + ':' + parse_quick_access_cmd(control)
    elif (cmdType == 2):
        cmd_str = cmd_types.get(cmdType, 'Unknown') + ':' + parse_accesss_memory_cmd(control)
    else:
        cmd_str = 'Unknown Command'
    cmd_str = '\n        ' + cmd_str
    return cmd_str


cmderrs = {
    0: 'No Error',
    1: 'Busy',
    2: 'Not supported',
    3: 'Exception',
    4: 'Halt/Resume',
    5: 'Bus error',
    7: 'Others'
}
def parse_abstract_control_status(data, isWrite):
    datacount = data & 0xF 
    cmderr = (data >> 8) & 0x7 
    busy  = (data >> 12) & 1 
    progbufsize = (data >> 24) & 0x1f 
    cmdStr = ''
    if isWrite:
        cmdStr += '\n        ' + 'Clear errors: ' + hex(cmderr)
    else:
  #      cmdStr = '\n        ' + 'datacount: ' + hex(datacount)

        cmdStr += '\n        ' + 'cmderr: ' + cmderrs.get(cmderr, 'Unknown')
  #      cmdStr += \
  #      '\n        ' + 'busy: ' + hex(busy) + \
  #      '\n        ' + 'progbufsize: ' + hex(progbufsize)
    return cmdStr

def parse_dmcontrol(data, isWrite):
    dmactive = data & 1 
    ndmreset = (data >> 1) & 1 
    clrresethalreq = (data >> 2) & 1
    setresethalreq = (data >> 3) & 1
    hartselhi = (data >> 6) & 0x7F
    hartsello = (data >> 16) & 0x7F
    hasel = (data >> 26) & 1 
    ackhavereset = (data >> 28) & 1 
    hartreset = (data >> 29) & 1 
    resumereq = (data >> 30) & 1 
    haltreq = (data >> 31) & 1
    if isWrite:
        cmdStr = '\n        ' + 'dmactive: ' + str(dmactive)
        if (clrresethalreq == 1):
            cmdStr += ('\n        ' + 'clrresethalreq: ' + str(clrresethalreq))
        if (setresethalreq == 1):
            cmdStr += ('\n        ' + 'setresethalreq: ' + str(setresethalreq))
        cmdStr += ('\n        '  + 'hartsel: ' + str(hartsello | (hartselhi <<10)))
        cmdStr += ('\n        '  + 'hasel: ' + str(hasel))
        if (ackhavereset == 1):
            cmdStr += ('\n        '  + 'ackhavereset: ' + str(ackhavereset))
        cmdStr += ('\n        '  + 'hartreset: ' + str(hartreset))
        if (resumereq == 1):
            cmdStr += ('\n        '  + 'resumereq: ' + str(resumereq))
        cmdStr += ('\n        '  + 'haltreq: ' + str(haltreq))
    else:
        cmdStr = '\n        ' + 'dmactive: ' + str(dmactive)
        cmdStr += ('\n        '  + 'hartsel: ' + str(hartsello | (hartselhi <<10)))
        cmdStr += ('\n        '  + 'hasel: ' + str(hasel))
        cmdStr += ('\n        '  + 'hartreset: ' + str(hartreset))
    return cmdStr


def parse_dmstatus(data, isWrite):
    version = data & 0xF
    confstrptrvalid = (data >> 4) & 1
    hasresethalreq = (data >> 5) & 1 
    authbusy = (data >> 6) & 1 
    authenticated = (data >> 7) & 1 
    anyhalted = (data >> 8 ) & 1 
    allhalted = (data >> 9) & 1 
    anyrunning = (data >> 10) & 1 
    allrunning = (data >> 11) & 1 
    anyunavail = (data >> 12) & 1 
    allunavail = (data >> 13) & 1 
    anynonexistent = (data >> 14) & 1 
    allnonexistent = (data >> 15) & 1
    anyresumeack = (data >> 16) & 1 
    allresumeack = (data >> 17) & 1 
    anyhavereset = (data >> 18) & 1 
    allhavereset = (data >> 19) & 1
    impebreak = (data >> 22) & 1
    cmdStr = ''
    """     
    if (version == 0):
        versionStr = 'No'
    elif (version == 1):
        versionStr = '0.11'
    elif (version == 2):
        versionStr = '0.13'
    else:
        versionStr = 'Unknown'
    cmdStr += '\n        ' + 'version: ' + versionStr 
    if (confstrptrvalid == 1):
        cmdStr += ('\n        ' + 'confstrptrvalid: ' + str(confstrptrvalid))
    if (hasresethalreq == 1):
        cmdStr += ('\n        ' + 'hasresethalreq: ' + str(hasresethalreq))
    if (authbusy == 1):
        cmdStr += ('\n        ' + 'authbusy: ' + str(authbusy))
    
    if (authenticated == 1):
        cmdStr += ('\n        ' + 'authenticated: ' + str(authenticated))'
    """
    if (anyhalted == 1):
        cmdStr += ('\n        ' + 'anyhalted: ' + str(anyhalted))
    if (allhalted == 1):
        cmdStr += ('\n        ' + 'allhalted: ' + str(allhalted))
    if (anyrunning == 1):
        cmdStr += ('\n        ' + 'anyrunning: ' + str(anyrunning))
    if (allrunning == 1):
        cmdStr += ('\n        ' + 'allrunning: ' + str(allrunning))
    """
    if (anyunavail == 1):
        cmdStr += ('\n        ' + 'anyunavail: ' + str(anyunavail))
    if (allunavail == 1):
        cmdStr += ('\n        ' + 'allunavail: ' + str(allunavail))
    if (anynonexistent == 1):
        cmdStr += ('\n        ' + 'anynonexistent: ' + str(anynonexistent))
    if (allnonexistent == 1):
        cmdStr += ('\n        ' + 'allnonexistent: ' + str(allnonexistent))
    if (anyresumeack == 1):
        cmdStr += ('\n        ' + 'anyresumeack: ' + str(anyresumeack))
    if (allresumeack == 1):
        cmdStr += ('\n        ' + 'allresumeack: ' + str(allresumeack))
    if (anyhavereset == 1):
        cmdStr += ('\n        ' + 'anyhavereset: ' + str(anyhavereset))
    if (allhavereset == 1):
        cmdStr += ('\n        ' + 'allhavereset: ' + str(allhavereset))
    if (impebreak == 1):
        cmdStr += ('\n        ' + 'impebreak: ' + str(impebreak))
    """
    return cmdStr


def parse_abstractauto(data, isWrite):
    autoexecdata = data & 0x3FF
    autoexecprogbuf = (data >> 16) & 0xFFFF
    cmdStr = ''
    if isWrite:
        cmdStr = '\n        ' + 'autoexecdata: ' + hex(autoexecdata)  
        cmdStr += ('\n        ' + 'autoexecprogbuf: ' + hex(autoexecprogbuf))  
    else:
        if (data != 0):
            if (autoexecdata > 0):
                cmdStr += '\n        ' + 'autoexecdata: ' + hex(autoexecdata)  
            if (autoexecdata):
                cmdStr += ('\n        ' + 'autoexecprogbuf: ' + hex(autoexecprogbuf))
        else:
            cmdStr = '\n        ' + 'abstractauto is NOT supported'
    return cmdStr

def parse_sbcs(data, isWrite):
    sbaccess8 = data & 1 
    sbaccess16 = (data >> 1) & 1
    sbaccess32 = (data >> 2) & 1
    sbaccess64 = (data >> 3) & 1
    sbaccess128 = (data >> 4) & 1
    sbsize = (data >> 5) & 0x7f 
    sberror = (data >> 12) & 7
    sbreadondata = (data >> 15) & 1
    sbautoincrement = (data >> 16) & 1
    sbaccess = (data >> 17) & 7
    sbreadonaddr = (data >> 20) & 1
    sbbusy = (data >> 21) & 1
    sbbusyerror = (data >> 22) & 1
    sbversion = (data >> 29) & 7

    if isWrite:
        return ''
    else:
        cmdStr = '\n        '
        if (sbsize == 0):
            cmdStr += " System Bus is NOT support"
        else:
            cmdStr += hex(data)
        return cmdStr


def parse_dmi_reg_data(reg, data, isWrite):
    cmdStr = ''
    if (reg == 0x17): #command
        cmdStr = parse_abstract_cmd(data, isWrite)
    elif (reg == 0x16): #abstractcs
        cmdStr = parse_abstract_control_status(data, isWrite)
    elif (reg == 0x11): #dmstatus
        cmdStr = parse_dmstatus(data, isWrite)
    elif (reg == 0x10): #dmcontrol
        cmdStr = parse_dmcontrol(data, isWrite)
    elif (reg == 0x18): #abstractauto
        cmdStr = parse_abstractauto(data, isWrite)
    elif (reg >= 0x20) and (reg <= 0x2F): #progbuf0-progbuf15
        cmdStr = parse_rv32_inst(data)
    elif (reg >= 0x4) and (reg <= 0x0F): # data0-data11
        '''
        if isWrite:
            cmdStr =  parse_rv32_inst(data)
        '''
    elif (reg == 0x38):
        cmdStr = parse_sbcs(data, isWrite)
    else:
        cmdStr ='\n        Unsupported yet, to be added'  
    return cmdStr

def parse_mdi_data_in(dataIn):
    op = dataIn & 0x03
    data = (dataIn >> 2) & 0xffffffff
    addr = (dataIn >> 34) & 0x7F
    OpInfo = op_val.get(op, "Reserved")
    DataInfo = hex(data)
    regInfo = riscv_dm_regs.get(addr, ['unknown', 'Unknown'])[0]
    dataInInfo = OpInfo
    if (op == 2): # Write
        dataInInfo += ' ' + regInfo + ' ---> ' + DataInfo
        dataInInfo += parse_dmi_reg_data(addr, data, True)
    elif (op == 1): # Read
        dataInInfo += ' ' + regInfo + ' <---'
    return op,data,addr,dataInInfo

def parse_mdi_data_out(dataOut):
    ack = dataOut & 0x03
    data = (dataOut >> 2) & 0xffffffff
    addr = (dataOut >> 34) & 0x7F
    AckInfo = ack_val.get(ack, "Reserved")
    if (ack == 0):
        DataInfo = hex(data)
        regInfo = riscv_dm_regs.get(addr, ['unknown', 'Unknown'])[0]
        regDataInfo = parse_dmi_reg_data(addr, data, False)
        if (regDataInfo.endswith('No Error')):
            dataOutInfo = '.'
        else:
            dataOutInfo = 'Read: ' + regInfo + ' = ' + DataInfo + regDataInfo

    else:
        dataOutInfo = AckInfo
    return ack,data,addr,dataOutInfo


def get_tap_count(dataIn, dataOut):
    count = 0
    while dataOut != dataIn:
        count = count + 1
        dataOut = dataOut >> 1